/*
 *  Project     : QKeyboardStatistic
 *  Author      : Durachenko Aleksey V. <durachenko.aleksey@gmail.com>, 2011
 *  Licence     : GNU GENERAL PUBLIC LICENSE Version 2
 */
#include "cmainwindow.h"

#include <QtCore/qmath.h>
#include <QMessageBox>
#include <QDir>
#include "csettingsdialog.h"
#include "caboutdialog.h"
#include "global.h"

// ----------------------------------------------------------------------
CMainWindow::CMainWindow(QWidget * parent, Qt::WFlags f) : QMainWindow(parent, f)
{
    // init gui
    setupUi(this);

    // install window title
    setWindowTitle(G_FULL_PROGRAM_NAME());

    // initialization tray icon
    QMenu* systemTrayMenu = new QMenu(this);
    systemTrayMenu->addAction(action_ShowHide);
    systemTrayMenu->addAction(action_Settings);
    systemTrayMenu->addSeparator();
    systemTrayMenu->addAction(action_Quit);

    mSystemTrayIcon = new QSystemTrayIcon(QIcon(":/icons/wicon.png"), this);
    mSystemTrayIcon->setVisible(true);
    mSystemTrayIcon->setContextMenu(systemTrayMenu);
    mSystemTrayIcon->setToolTip(G_FULL_PROGRAM_NAME());

    connect(mSystemTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
            SLOT(slot_mSystemTrayIcon_activated(QSystemTrayIcon::ActivationReason)));

    connect(action_AboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));

    // read the stored settings
    readSettings();
}


// ----------------------------------------------------------------------
CMainWindow::~CMainWindow()
{
    // write the current settings
    writeSettings();
}


// ----------------------------------------------------------------------
void CMainWindow::readSettings()
{
    G_SETTINGS_INIT();
    restoreGeometry(settings.value("CMainWindow/geometry", saveGeometry()).toByteArray());

    // is it first run, then initialization default values
    if (!settings.contains("Database"))
    {
        settings.setValue("Database", QDir::homePath() + QDir::separator() + QString("QKeyboardStatisticDatabase.db"));
    }
    if (!settings.contains("PixelResolution"))
    {
        settings.setValue("PixelResolution", 0.228);
    }
    if (!settings.contains("AutoSavePeriod"))
    {
        settings.setValue("AutoSavePeriod", 5);
    }
    if (!settings.contains("SpyKeyboard"))
    {
        settings.setValue("SpyKeyboard", true);
    }
    if (!settings.contains("SpyMouse"))
    {
        settings.setValue("SpyMouse", true);
    }

    // restore values from settings
    QString fileName = settings.value("Database").toString();
    int autoSavePeriod = settings.value("AutoSavePeriod").toInt();
    mPixelResulution = settings.value("PixelResolution").toDouble();
    bool spyKeyboard = settings.value("SpyKeyboard").toBool();
    bool spyMouse = settings.value("SpyMouse").toBool();

    // create database object
    CDatabase* database = new CDatabase(this);
    if (!database->open(fileName))
    {
        QMessageBox::warning(this, tr("Warning"), tr("Can't open database. Please select new location in the configuration dialog."));
    }

    // configure statistic object
    mStatistic = new CStatistic(database, autoSavePeriod, this);

    // IMPORTANT! CDatabase destroyed after CStatistic
    database->setParent(mStatistic);

    // udpate view if statistic changed
    connect(mStatistic, SIGNAL(keyStatisticChanged()), this, SLOT(slot_updateKeyCounter()));
    connect(mStatistic, SIGNAL(mousePressStatisticChanged()), this, SLOT(slot_updateMousePressCounter()));
    connect(mStatistic, SIGNAL(mileageStatisticChanged()), this, SLOT(slot_updateMileage()));

    // restore visible state
    setVisible(!settings.value("isVisible", true).toBool());
    on_action_ShowHide_triggered();

    // start the spy module
    mHookThread = new CHookThread(spyKeyboard, spyMouse, this, this);
}


// ----------------------------------------------------------------------
void CMainWindow::writeSettings()
{
    G_SETTINGS_INIT();
    settings.setValue("CMainWindow/geometry", saveGeometry());
    settings.setValue("isVisible", isVisible());
}


// ----------------------------------------------------------------------
void CMainWindow::on_action_ShowHide_triggered()
{
    setVisible(!isVisible());

    if (!isVisible())
    {
        action_ShowHide->setText(tr("&Show"));
    }
    else
    {
        action_ShowHide->setText(tr("&Hide"));

        // update text fields
        slot_updateKeyCounter();
        slot_updateMousePressCounter();
        slot_updateMileage();
    }
}


// ----------------------------------------------------------------------
void CMainWindow::on_action_Settings_triggered()
{
    CSettingsDialog dialog(this);
    if (dialog.exec() == QDialog::Accepted)
    {
        G_SETTINGS_INIT();
        mPixelResulution = settings.value("PixelResolution").toDouble();
        slot_updateMileage();
    }
}


// ----------------------------------------------------------------------
void CMainWindow::on_action_Quit_triggered()
{
    qApp->quit();
}


// ----------------------------------------------------------------------
void CMainWindow::on_action_About_triggered()
{
    CAboutDialog dialog(this);
    dialog.exec();
}


// ----------------------------------------------------------------------
void CMainWindow::slot_mSystemTrayIcon_activated(QSystemTrayIcon::ActivationReason reason)
{
    if (reason == QSystemTrayIcon::DoubleClick)
    {
        on_action_ShowHide_triggered();
    }
}


// ----------------------------------------------------------------------
void CMainWindow::closeEvent(QCloseEvent* event)
{
    on_action_ShowHide_triggered();
    event->ignore();
}


// ----------------------------------------------------------------------
void CMainWindow::customEvent(QEvent* event)
{
    if (event->type() == G_KeyPressEventId)
    {
        mStatistic->incKeyCount();
    }
    else if (event->type() == G_MousePressEventId)
    {
        mStatistic->incMousePressCount();
    }
    else if (event->type() == G_MouseMoveEventId)
    {
        // calculate mouse distance as dist = sqrt(x^2+y^2)
        static QPoint old = reinterpret_cast<QMouseEvent*>(event)->pos();
        QPoint current = reinterpret_cast<QMouseEvent*>(event)->pos();
        QPoint p = current - old;
        qint64 mileage = qAbs(qRound64(qSqrt(p.x()*p.x()+p.y()*p.y())));
        old = current;
        mStatistic->incMileage(mileage);
    }

    QMainWindow::customEvent(event);
}


// ----------------------------------------------------------------------
void CMainWindow::slot_updateKeyCounter()
{
    if (isVisible())
    {
        lineEdit_KeyTotal->setText(createReadable(mStatistic->keyTotal()));
        lineEdit_KeyMonth->setText(createReadable(mStatistic->keyMonth()));
        lineEdit_KeyWeek->setText(createReadable(mStatistic->keyWeek()));
        lineEdit_KeyDay->setText(createReadable(mStatistic->keyDay()));
        lineEdit_KeyHour->setText(createReadable(mStatistic->keyHour()));
    }
}


// ----------------------------------------------------------------------
void CMainWindow::slot_updateMousePressCounter()
{
    if (isVisible())
    {
        lineEdit_MousePressTotal->setText(createReadable(mStatistic->mousePressTotal()));
        lineEdit_MousePressMonth->setText(createReadable(mStatistic->mousePressMonth()));
        lineEdit_MousePressWeek->setText(createReadable(mStatistic->mousePressWeek()));
        lineEdit_MousePressDay->setText(createReadable(mStatistic->mousePressDay()));
        lineEdit_MousePressHour->setText(createReadable(mStatistic->mousePressHour()));
    }
}


// ----------------------------------------------------------------------
void CMainWindow::slot_updateMileage()
{
    if (isVisible())
    {
        lineEdit_MileageTotal->setText(createMileage(mStatistic->mileageTotal()));
        lineEdit_MileageMonth->setText(createMileage(mStatistic->mileageMonth()));
        lineEdit_MileageWeek->setText(createMileage(mStatistic->mileageWeek()));
        lineEdit_MileageDay->setText(createMileage(mStatistic->mileageDay()));
        lineEdit_MileageHour->setText(createMileage(mStatistic->mileageHour()));
    }
}


// ----------------------------------------------------------------------
QString CMainWindow::createMileage(qint64 mileage)
{
    double value = mileage * mPixelResulution;
    QString postfix = QString();
    if (value < 1000.0)
    {
        postfix = tr("mm");
    }
    else if (value < 1000000.0)
    {
        value /= 1000.0;
        postfix = tr("m");
    }
    else
    {
        value /= 1000000.0;
        postfix = tr("km");
    }
    return QString::number(value, 'f', 3) + QString(" ") + postfix;
}


// ----------------------------------------------------------------------
QString CMainWindow::createReadable(qint64 value)
{
    QString postfix = QString();
    if (value < 1000)
    {
        return QString::number(value);
    }
    else if (value < 1000000)
    {
        return QString().sprintf("%dK %03d", (int)(value/1000), (int)(value%1000));
    }
    else
    {
        return QString().sprintf("%dM %03dK %03d", (int)(value/1000000), (int)((value%1000000)/1000), (int)(value%1000));
    }
    return QString();
}
